#include <PersonI.h>
#include <Ice/Ice.h>

using namespace std;
using namespace Demo;
using namespace IceUtil;

PersonPrx
PersonFactoryI::create(const string& name, const string& addr, const Ice::Current& c)
{
    Mutex::Lock lock(_lcMutex);

    PersonPrx p;
    PersonIPtr servant;
    try
    {
	servant = new PersonI(name, addr);
        p = PersonPrx::uncheckedCast(c.adapter->add(servant,
	                                            c.adapter->getCommunicator()->
						    stringToIdentity(name)));
    }
    catch(const Ice::AlreadyRegisteredException&)
    {
        throw PersonExists();
    }

    PMap::iterator i = _persons.begin();
    while(i != _persons.end())
    {
        if(i->second->_isZombie())
	{
	    _persons.erase(i++);
	}
	else
	{
	    ++i;
	}
    }
    _persons[name] = servant;
    return p;
}

PersonPrx
PersonFactoryI::find(const string& name, const Ice::Current& c) const
{
    Mutex::Lock lock(_lcMutex);

    PersonPrx p;
    Ice::Identity id = c.adapter->getCommunicator()->stringToIdentity(name);
    if(c.adapter->find(id))
    {
	p = PersonPrx::uncheckedCast(c.adapter->createProxy(id));
    }
    return p;
}

PersonSeq
PersonFactoryI::list(const Ice::Current& c) const
{
    Mutex::Lock lock(_lcMutex);

    PersonSeq ps;
    PMap::iterator i = _persons.begin();
    while(i != _persons.end())
    {
        if(i->second->_isZombie())
	{
	    _persons.erase(i++);
	}
	else
	{
	    ps.push_back(PersonPrx::uncheckedCast(c.adapter->createProxy(c.adapter->
									 getCommunicator()->
									 stringToIdentity(i->first))));
	    ++i;
	}
    }
    return ps;
}

PersonI::PersonI(const string& name, const string& addr)
    : _name(name), _addr(addr), _destroyed(false)
{
}

string
PersonI::name(const Ice::Current&) const
{
    Mutex::Lock lock(_m);
    if(_destroyed)
    {
        throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
    return _name;
}

string
PersonI::getAddress(const Ice::Current&) const
{
    Mutex::Lock lock(_m);
    if(_destroyed)
    {
        throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
    return _addr;
}

void
PersonI::setAddress(const string& addr, const Ice::Current&)
{
    cout << "setAddress() called" << endl;

    // If you want to see the race condition that is mentioned in
    // the article, enable the delay below. This causes destroy()
    // to run to completion before this operation can acquire the lock.
    // In that case, the _destroyed flag will be set and we
    // throw ObjectNotExistException instead of operating on a
    // destroyed Ice object.

    ThreadControl::sleep(Time::seconds(2));

    Mutex::Lock lock(_m);
    if(_destroyed)
    {
	cout << "setAddress() returning with ObjectNotExistException" << endl;
        throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }

    _addr = addr;

    // Sleep five seconds so we can follow the trace messages.
    ThreadControl::sleep(Time::seconds(5));

    cout << "setAddress() returning normally" << endl;
}

void
PersonI::destroy(const Ice::Current& c)
{
    cout << "destroy() called, waiting until servant is idle" << endl;

    Mutex::Lock lock(_m);
    if(_destroyed)
    {
	cout << "destroy() returning with ObjectNotExistException" << endl;
	throw Ice::ObjectNotExistException(__FILE__, __LINE__);
    }
    _destroyed = true;

    c.adapter->remove(c.id);

    cout << "destroy() returning normally" << endl;
}

bool
PersonI::_isZombie() const
{
    Mutex::Lock lock(_m);
    return _destroyed;
}
